/*
  Copyright 2006 by Sean Luke and George Mason University
  Licensed under the Academic Free License version 3.0
  See the file "LICENSE" for more information
 */

package ec.app.deferEvalCoevolve1;

import ec.*;
import ec.coevolve.GroupedProblemForm;
import ec.steadystate.SteadyStateEvaluator;
import ec.util.*;

/** 
 * MPCoevoEvalVar2.java
 *

 <p>MPCoevoEvalVar2 is an Evaluator which performs <i>competitive or cooperative multi-population
 coevolution</i>.  Competitive coevolution is where individuals' fitness is determined by
 testing them against individuals from other subpopulation.  Cooperative coevolution is where individuals
 form teams together with members of other subpopulations, and the individuals' fitness is computed based
 on the performance of such teams.  This evaluator assumes that the problem can only evaluate groups of
 individuals containing one individual from each subpopulation.  Individuals are evaluated regardless of
 whether or not they've been evaluated in the past.

 <p>Your Problem is responsible for updating up the fitness appropriately with values usually obtained
 from teaming up the individual with different partners from the other subpopulations.
 MPCoevoEvalVar2 expects to use Problems which adhere to the GroupedProblemForm
 interface, which defines a new evaluate(...) function, plus a preprocess(...) and postprocess(...) function.

 <p>This coevolutionary evaluator is single-threaded -- maybe we'll hack in multithreading later.  It allows
 any number of subpopulations (implicitly, any number of individuals being evaluated together). The order of
 individuals in the subpopulation may be changed during the evaluation process.

 <p><b>Parameters</b><br>
 <table>
 <tr><td valign=top><i>base.</i><tt>subpop.X.num-rand-ind</tt><br>
 <font size=-1> int &gt;= 0</font></td>
 <td valign=top>(the number of random individuals from subpopulation X to be selected as partners for evaluating individuals in other subpopulations  -- individuals are selected at random, with replacement, and they are usually different for each of the individuals in the other subpopulations)
 </td></tr>

 <tr><td valign=top><i>base.</i><tt>subpop.X.num-elites</tt><br>
 <font size=-1> int &gt;= 0</font></td>
 <td valign=top>(the number of elite individuals from subpopulation X to be selected as partners for evaluating individuals in other subpopulations)
 </td></tr>

 <tr><td valign=top><i>base.</i><tt>subpop.X.num-ind</tt><br>
 <font size=-1> int &gt;= 0</font></td>
 <td valign=top>(the number of individuals from subpopulation X in the previous generation to be selected as partners for evaluating individuals in other subpopulations  -- individuals are selected, with replacement, by using SelectionMethods described next, and they are usually different for each of the individuals in the other subpopulations)
 </td></tr>

 <tr><td valign=top><i>base.</i><tt>subpop.X.select</tt><br>
 <font size=-1> instance of ec.SelectionMethod</font></td>
 <td valign=top>(the SelectionMethod used to select partners from the individuals in subpopulation X at the previous generation)
 </td></tr>

 </table>

 *
 * @author Liviu Panait
 * @version 2.0 
 */

/**
 * @author RothmD01
 * 
 */
public class MPCoevoEvalVar2 extends SteadyStateEvaluator {
	// the number of elite partners selected from the previous generation
	public static final String P_NUM_ELITE = "num-elites";

	// the number of other partners selected from the previous generation
	public final static String P_NUM_IND = "num-ind";
	// the number of random partners selected from the current generation
	public static final String P_NUM_RAND_IND = "num-rand-ind";
	// the selection method used to select the other partners from the previous
	// generation
	public static final String P_SELECTIONMETHOD = "select";
	// the preamble for selecting partners from each subpopulation
	public static final String P_SUBPOP = "subpop";
	Individual[][] eliteIndividuals;
	private Individual[] mates = null;
	protected int[] numElite;

	protected int[] numInd;
	protected int[] numRand;
	// the total number of partners (numRand+numElite+numInd)
	protected int[] numSelected;

	public Population previousPopulation;
	public SelectionMethod[] selectionMethod;

	private int[] subpops = null;
	private boolean[] updates = null;

	public void afterCoevolutionaryEvaluation(final EvolutionState state,
			final Population population, final GroupedProblemForm prob) {
		// deal with the previous population
		previousPopulation = (Population) (state.population.emptyClone());
		for (int i = 0; i < previousPopulation.subpops.length; i++)
			for (int j = 0; j < previousPopulation.subpops[i].individuals.length; j++)
				previousPopulation.subpops[i].individuals[j] = (Individual) (state.population.subpops[i].individuals[j]
						.clone());
		return;
	}

	public void beforeCoevolutionaryEvaluation(final EvolutionState state,
			final Population population, final GroupedProblemForm prob) {
		return;
	}

	@Override
	public void evaluatePopulation(final EvolutionState state) {
		beforeCoevolutionaryEvaluation(state, state.population,
				(GroupedProblemForm) p_problem);

		((GroupedProblemForm) p_problem).preprocessPopulation(state,
				state.population);
		// performCoevolutionaryEvaluation( state, state.population,
		// (GroupedProblemForm)p_problem );
		((GroupedProblemForm) p_problem).postprocessPopulation(state,
				state.population);

		afterCoevolutionaryEvaluation(state, state.population,
				(GroupedProblemForm) p_problem);
	}

	public int getNumPartners(int subpop) {
		return numSelected[subpop];
	}

	public int getNumEliteIndividuals(int subpop) {
		return numElite[subpop];
	}

	public int getNumPreviousGenerationPartners(int subpop) {
		return numInd[subpop];
	}

	public int getNumRandomPartners(int subpop) {
		return numRand[subpop];
	}

	@Override
	public boolean runComplete(final EvolutionState state) {
		return false;
	}

	@Override
	public void setup(final EvolutionState state, final Parameter base) {
		super.setup(state, base);

		// at this point, we do not know the number of subpopulations, so we
		// read it as well from the parameters file
		Parameter tempSubpop = new Parameter(ec.Initializer.P_POP)
				.push(ec.Population.P_SIZE);
		int numSubpopulations = state.parameters.getInt(tempSubpop, null, 0);
		if (numSubpopulations <= 0)
			state.output.fatal(
					"Parameter not found, or it has a non-positive value.",
					tempSubpop);

		selectionMethod = new SelectionMethod[numSubpopulations];
		numElite = new int[numSubpopulations];
		numRand = new int[numSubpopulations];
		numInd = new int[numSubpopulations];
		numSelected = new int[numSubpopulations];

		for (int i = 0; i < numSubpopulations; i++) {
			numElite[i] = state.parameters.getInt(base.push(P_SUBPOP).push(
					"" + i).push(P_NUM_ELITE), null, 0);
			if (numElite[i] < 0)
				state.output.fatal(
						"Parameter not found, or it has an incorrect value.",
						base.push(P_SUBPOP).push("" + i).push(P_NUM_ELITE));

			numRand[i] = state.parameters.getInt(base.push(P_SUBPOP).push(
					"" + i).push(P_NUM_RAND_IND), null, 0);
			if (numRand[i] < 0)
				state.output.fatal(
						"Parameter not found, or it has an incorrect value.",
						base.push(P_SUBPOP).push("" + i).push(P_NUM_RAND_IND));

			numInd[i] = state.parameters.getInt(base.push(P_SUBPOP)
					.push("" + i).push(P_NUM_IND), null, 0);
			if (numInd[i] < 0)
				state.output.fatal(
						"Parameter not found, or it has an incorrect value.",
						base.push(P_SUBPOP).push("" + i).push(P_NUM_IND));
			else if (numInd[i] > 0) {
				selectionMethod[i] = (SelectionMethod) (state.parameters
						.getInstanceForParameter(base.push(P_SUBPOP).push(
								"" + i).push(P_SELECTIONMETHOD), null,
								SelectionMethod.class));
				selectionMethod[i].setup(state, base.push(P_SUBPOP)
						.push("" + i).push(P_SELECTIONMETHOD));
			}

			numSelected[i] = numElite[i] + numRand[i] + numInd[i];

			if (numSelected[i] <= 0)
				state.output
						.fatal("The total number of partners to be selected from subpopulation "
								+ i + " should be > 0.");

		}
	}

}
